﻿namespace KeyStone.EventHub
{
   using System;
   using System.Collections.Generic;
   using System.Collections.ObjectModel;
   using System.Linq;
   using System.Windows;
   using System.Windows.Threading;

   /// <summary>
   /// Class EventBase.
   /// </summary>
   /// <typeparam name="TPayload">The event Payload to deliver</typeparam>
   internal sealed class EventBase<TPayload> where TPayload : Payload
  {
      /// <summary>
      /// Gets the unique event identifier.
      /// </summary>
      /// <value>The unique event identifier.</value>
      public Guid UniqueEventId { get; private set; }

      /// <summary>
      /// Gets or sets the name of the event.
      /// </summary>
      /// <value>The name of the event.</value>
      public string EventName { get; set; }

      /// <summary>
      /// Gets or sets the subscribers.
      /// </summary>
      /// <value>The subscribers.</value>
      public ObservableCollection<Action<TPayload>> Subscribers { get; set; }

      /// <summary>
      /// Gets or sets the last data published
      /// </summary>
      /// <value>The last data.</value>
      public List<PublishedData> LastPublishedData { get; set; }

      /// <summary>
      /// Initializes a new instance of the <see cref="EventBase{TPayload}" /> class.
      /// </summary>
      public EventBase()
      {
         LastPublishedData = new List<PublishedData>();
         UniqueEventId = Guid.NewGuid();
         Subscribers = new ObservableCollection<Action<TPayload>>();
         EventName = GetType().Name;
      }

      /// <summary>
      /// Executes the specified data.
      /// </summary>
      /// <param name="sync">The synchronization object</param>
      /// <param name="runOnUiThread">if set to <c>true</c> run on UI thread.</param>
      public void Execute(bool runOnUiThread = true)
      {
            Subscribers.ToList().ForEach(subscriber =>
            {
               LastPublishedData.ToList().ForEach(payload =>
               {
                  ExecuteDelegate(() =>
                  {
                     subscriber.Invoke((TPayload)payload.Data);
                  }, runOnUiThread);

                  payload.Fulfilled = true;
               });
            });
      }

      /// <summary>
      /// Executes the delegate.
      /// </summary>
      /// <param name="process">The process.</param>
      /// <param name="runOnUiThread">if set to <c>true</c> [run on UI thread].</param>
      private void ExecuteDelegate(Action process, bool runOnUiThread = true)
      {
         if (runOnUiThread)
         {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, process);
         }
         else
         {
            process();
         }
      }
   }
}